package com.google.code.expr.lang;

import java.io.FileReader;

/**
 * This class is generally not used.  It will parse out the
 * EXPR file into an object structure.  this could be used
 * for running, only this is not efficient.  It is left
 * here for compilers to use.
 * 
 * @author critchie
 *
 */
public class EXPRBlockParser {

	public EXPRBlockParser() {
		super();
	}

	/**
	 * Parses out the entire file using the atom reader into
	 * an EXPRBlock object.  It's main job is to sort the
	 * conditionals and their child blocks.
	 * 
	 * @param pReader
	 * @return
	 * @throws EXPRParseException
	 */
	public EXPRBlock parse(EXPRAtomReader pReader) throws EXPRParseException {
		EXPRBlock block = new EXPRBlock();
		EXPRAtom nextAtom = pReader.read();
		while (nextAtom!=null) {
			if (nextAtom instanceof EXPRConditionalCase) {
				EXPRConditionalCase cond = (EXPRConditionalCase)nextAtom;
				if (cond.getType().equals(EXPRConditonalType.IF)) {
					block.add(sortConditional(cond,pReader));
				}
				else {
					throw new EXPRParseException(cond, "Encountered "+cond.getType()+" before a "+EXPRConditonalType.IF);
				}
			}
			else if (nextAtom instanceof EXPRBlockComponent) {
				block.add((EXPRBlockComponent)nextAtom);
			}
			else {
				throw new EXPRParseException(nextAtom, "Encountered unexpected type "+nextAtom.getClass().getName());
			}
			nextAtom = pReader.read();
		}
		return block;
	}

	protected EXPRConditional sortConditional(EXPRConditionalCase pIf, EXPRAtomReader pReader) throws EXPRParseException {
		EXPRConditional cond = new EXPRConditional();
		EXPRConditionalCase condCase = pIf;
		EXPRBlock block = new EXPRBlock();
		condCase.setBlock(block);
		cond.addCase(condCase);
		boolean encountedElse = false;
		EXPRAtom nextAtom = pReader.read();
		while (nextAtom!=null && !(nextAtom instanceof EXPRConditionalCase && ((EXPRConditionalCase)nextAtom).getType().equals(EXPRConditonalType.ENDIF))) {
			if (nextAtom instanceof EXPRConditionalCase) {
				EXPRConditionalCase nextCond = (EXPRConditionalCase)nextAtom;
				if (nextCond.getType().equals(EXPRConditonalType.IF)) {
					//a child if found
					block.add(sortConditional(nextCond,pReader));
				}
				else {
					//the next elseif or else
					if (nextCond.getType().equals(EXPRConditonalType.ELSE)) {
						if (encountedElse) {
							throw new EXPRParseException(nextAtom, "Encountered a second "+EXPRConditonalType.ELSE);
						}
						encountedElse = true;
					}
					if (nextCond.getType().equals(EXPRConditonalType.ELSEIF)) {
						if (encountedElse) {
							throw new EXPRParseException(nextAtom, "Encountered an "+EXPRConditonalType.ELSEIF+" after an "+EXPRConditonalType.ELSE);
						}
					}
					condCase = nextCond;
					block = new EXPRBlock();
					condCase.setBlock(block);
					cond.addCase(condCase);
				}
			}
			else if (nextAtom instanceof EXPRBlockComponent) {
				block.add((EXPRBlockComponent)nextAtom);
			}
			else {
				throw new EXPRParseException(nextAtom, "Encountered unexpected type "+nextAtom.getClass().getName());
			}
			nextAtom = pReader.read();
		}
		if (nextAtom==null) {
			throw new EXPRParseException(pIf, "Missing corresponding "+EXPRConditonalType.ENDIF+" of an "+EXPRConditonalType.IF);
		}
		return cond;
	}

	
	public static void main(String[] pVars) throws Exception {
		FileReader fis = new FileReader("test/simple.expr");
		EXPRAtomReader reader = new EXPRAtomReader(fis);
		reader.setTrimStream(true);
		EXPRBlockParser parser = new EXPRBlockParser();
		EXPRBlock rootBlock = parser.parse(reader);
		System.out.println(rootBlock);
	}
}
